#pragma once
#include <d3d9.h>
#include "texture_quad.h"

#define HGE_CALL  __stdcall

class D3D9App {
public:
	using hgeGAPI = IDirect3D9;

	using hgeGAPIDevice = IDirect3DDevice9;
	using hgeGAPIVertexBuffer = IDirect3DVertexBuffer9;
	using hgeGAPIIndexBuffer = IDirect3DIndexBuffer9;
	using hgeGAPITexture = IDirect3DTexture9;
	using hgeGAPISurface = IDirect3DSurface9;
	using hgeGAPIViewport = D3DVIEWPORT9;
	using hgeGAPIAdapterIdentifier = D3DADAPTER_IDENTIFIER9;
	using hgeGAPICaps = D3DCAPS9;

	using HTEXTURE = IDirect3DTexture9*;
	using HTARGET = size_t;
	using HSHADER = size_t;

	using hgeColor32 = uint32_t;

	//
	// HGE_FPS system state special constants
	//
	enum {
		HGEFPS_UNLIMITED = 0,
		HGEFPS_VSYNC = -1,
	};


	//
	// HGE Primitive type constants
	//
	enum {
		HGEPRIM_LINES = 2,
		HGEPRIM_TRIPLES = 3,
		HGEPRIM_QUADS = 4,
	};


	//
	// HGE Blending constants
	//
	enum hgeBlendMode : uint32_t {
		BLEND_COLORADD = 1 << 0, BLEND_COLORMUL = 0,
		BLEND_ALPHABLEND = 1 << 1, BLEND_ALPHAADD = 0,
		BLEND_ZWRITE = 1 << 2, BLEND_NOZWRITE = 0,

		// Darken does real color multiplication, white source pixels don't change destination, while
		// black source pixels make destination completely black
		// Use example: http://relishgames.com/forum/index.php?p=/discussion/5799/darken-screen-plus-uneffected-hole/p1
		BLEND_DARKEN = 1 << 3,
		BLEND_BLACKEN = BLEND_DARKEN, /* synonym for darken */
		BLEND_DEFAULT = (BLEND_COLORMUL | BLEND_ALPHABLEND | BLEND_NOZWRITE),
		BLEND_DEFAULT_Z = (BLEND_COLORMUL | BLEND_ALPHABLEND | BLEND_ZWRITE),
	};

	//
	// HGE Vertex structure
	//
	using hgeVertex = Vertex;

	//
	// HGE Triple structure, represents a textured triangle
	//
	struct hgeTriple {
		hgeVertex v[3];
		HTEXTURE tex;
		hgeBlendMode blend;
	};


	//
	// HGE Quad structure
	//
	struct hgeQuad {
		hgeVertex v[4];
		HTEXTURE tex;
		hgeBlendMode blend;
	};


	// HTARGET internal structure
	struct CRenderTargetList {
		int width;
		int height;
		hgeGAPITexture* pTex;
		hgeGAPISurface* pDepth;
		CRenderTargetList* next;
	};

	// HTEXTURE internal structure
	struct CTextureList {
		HTEXTURE tex;
		int width;
		int height;
		CTextureList* next;
	};

public:
	// NOLINTNEXTLINE
	void HGE_CALL System_Log(const char* format, ...) { }

	// NOLINTNEXTLINE
	bool HGE_CALL Gfx_BeginScene(HTARGET target = 0);

	void HGE_CALL Gfx_EndScene();

	void HGE_CALL Gfx_Clear(hgeColor32 color);

	// NOLINTNEXTLINE
	void HGE_CALL Gfx_RenderLine(float x1, float y1, float x2, float y2,
		hgeColor32 color, float z = 0.5f);

	void HGE_CALL Gfx_RenderTriple(const hgeTriple* triple);

	void HGE_CALL Gfx_RenderQuad(const hgeQuad* quad);

	void HGE_CALL Gfx_RenderQuad(const hgeVertex* vertex, const HTEXTURE texture, hgeBlendMode blend);

	HTARGET HGE_CALL Target_Create(int width, int height, bool zbuffer);

	void HGE_CALL Target_Free(HTARGET target);

	HTEXTURE HGE_CALL Target_GetTexture(HTARGET target);

	HTEXTURE HGE_CALL Texture_Create(int width, int height);

	void HGE_CALL Texture_Free(HTEXTURE tex);

	// NOLINTNEXTLINE
	int HGE_CALL Texture_GetWidth(HTEXTURE tex, bool bOriginal = false);

	// NOLINTNEXTLINE
	int HGE_CALL Texture_GetHeight(HTEXTURE tex, bool bOriginal = false);

	// NOLINTNEXTLINE
	uint32_t* HGE_CALL Texture_Lock(HTEXTURE tex, bool bReadOnly = true, int left = 0,
		int top = 0, int width = 0, int height = 0);

	void HGE_CALL Texture_Unlock(HTEXTURE tex);


	static D3D9App* interface_get();

	void post_error(char const* error) { }


public:
	HWND hwnd_;

	int hgefps_;

	int screen_width_;
	int screen_height_;
	int screen_bpp_;
	bool windowed_;
	bool z_buffer_;
	bool texture_filter_;



	//-----------------------------------------------
	// Graphics
	//-----------------------------------------------
	D3DPRESENT_PARAMETERS* d3dpp_;

	D3DPRESENT_PARAMETERS d3dpp_windowed_;
public:
	RECT rect_windowed_;
	LONG style_windowed_;
// private:
	D3DPRESENT_PARAMETERS d3dpp_fullscreen_;
	RECT rect_fullscreen_;
	LONG style_fullscreen_;

	hgeGAPI* d3d_;
	hgeGAPIDevice* d3d_device_;
	hgeGAPIVertexBuffer* vertex_buf_;
	hgeGAPIIndexBuffer* index_buf_;

	hgeGAPISurface* screen_surf_;
	hgeGAPISurface* screen_depth_;
	CRenderTargetList* targets_;
	CRenderTargetList* cur_target_;

	D3DMATRIX view_matrix_;
	D3DMATRIX proj_matrix_;

	CTextureList* textures_;
	hgeVertex* vert_array_;

	int n_prim_;
	int cur_prim_type_;
	int cur_blend_mode_;
	HTEXTURE cur_texture_;
	HSHADER cur_shader_;

public:
	bool gfx_init();

// private:
	void gfx_done();

	bool gfx_restore();

	void adjust_window();

	bool init_lost();

	void render_batch(bool bEndScene = false);

	static int format_id(D3DFORMAT fmt);

	void set_blend_mode(hgeBlendMode blend);

	void set_projection_matrix(int width, int height);

private:
	D3D9App();
};

using Texture9 = IDirect3DTexture9;
extern D3D9App* g_pD3D9App;